# -*- coding: utf-8 -*-
import os
import commands
import json
import shutil
import string
from xml.dom import minidom , Node

TOOLS_DIR = os.path.dirname(os.path.realpath(__file__)) + "/tools"


def repack_AndroidMF(tray_name, inMF, outMF, meta_data):
    """ 重新打包修改AndroidManifest,主要修改meta_data和一些andorid权限
        meta_data = "BANGCLE_APP_KEY=test_47611,BANGCLE_SDK_VERSION=1"
        加的权限是固定写在jar里的,暂时不能自定义
    """

    if os.path.isfile(inMF):
        cmd = "java -jar %s/Android_axml.jar %s %s %s %s" % (TOOLS_DIR, tray_name, inMF, outMF, meta_data)
        a, b = commands.getstatusoutput(cmd)
        if a!=0:
            raise Exception('repack_AndroidMF fail:' + b)
    else:
        raise Exception('repack_AndroidMF fail inMF not exist')


def unzip_classes(classes_file, class_dir):
    """ 解压dex文件到smali文件
    """

    if os.path.isfile(classes_file):
        cmd = "%s/baksmali %s -o %s" % (TOOLS_DIR, classes_file, class_dir)
        a, b = commands.getstatusoutput(cmd)
        if a!=0:
            raise Exception('unzip_classes fail:' + b)
    else:
        raise Exception('unzip_classes fail classes_file not exist')


def unzip_apk(apkfile, outdir):
    """ 用系统自带unzip把文件解开,如果目的目录已经存在就报异常
    """

    #if os.path.isdir(outdir):
    #    raise Exception('the outdir has exist:' + outdir)
    if os.path.isfile(apkfile):
        cmd = "unzip -qo -P aaa %s -d %s" %(apkfile, outdir)
        a, b = commands.getstatusoutput(cmd)
        if a!=0:
            raise Exception('unzip_apk fail:' + b)
    else:
        raise Exception('unzip_apk fail apkfile not exist')

def pack_dex(smali_dir, classes_file):
    """ 把一个目录下的所有smali文件打包成一个dex文件,如果失败就抛出异常
    """

    if os.path.isdir(smali_dir):
        cmd = "%s/smali-512m %s -o %s" % (TOOLS_DIR, smali_dir, classes_file)
        a, b = commands.getstatusoutput(cmd)
        if a!=0:
            raise Exception('pack_dex fail:' + b)
    else:
        raise Exception('pack_dex fail:smali_dir not exist')

def zip_apk(apk, out, bangcleplugin, tmp, public_path, plugins_json, tray_name):
    """ 把一个apk重新打包生成新的apk,
        在assets里放入插件
        如果有需要在libs里放入需要的jar包
        把androidmanif.xml和class.dex用tmp里的代替
    """

    # params 传递给java程序的参数 
    # key为文件在zip包里的位置
    # value为文件在真实路径下的位置
    # 格式如下 key=value,key=value
    # 示例如下
    # AndroidManifest.xml=/tmp/AndroidManifest.xml,assest/bangcleplugin/dgc=/tmp/dgc
    params = {}

    #先保存dgc到本地
    with open(tmp + '/dgc','w') as f:
        f.write(json.dumps(plugins_json,indent=4))
    k = os.path.join(bangcleplugin, 'dgc')
    v = os.path.join(tmp, 'dgc')
    params[k] = v

    # plugin
    for plugin in plugins_json['plugins'].values():
        k = os.path.join(bangcleplugin, plugin['file_name'])
        v = os.path.join(public_path, 'plugins',str(plugin['version']), plugin['file_name'])
        params[k] = v

    # 千速sdk在assets中的资源文件
    if tray_name.lower() == 'qiansusdk':
        qiansusdk_assest = os.path.join(public_path, 'assets/qiansusdk')
        for root, dirnames, filenames in os.walk(qiansusdk_assest):
            for filename in filenames:
                k = os.path.join(root, filename).replace(qiansusdk_assest, 'assets')
                v = os.path.join(root, filename)
                params[k] = v        

    params['AndroidManifest.xml'] = os.path.join(tmp, 'AndroidManifest.xml')
    params['classes.dex'] = os.path.join(tmp, 'classes.dex')

    params_str = ','.join(['%s=%s' % (k, v) for k,v in params.items()])

    if os.path.isfile(apk):
        cmd = "java -jar %s/inject_zip.jar %s %s %s" % (TOOLS_DIR, params_str, apk, out)
        a, b = commands.getstatusoutput(cmd)
        if a!=0:
            raise Exception('zip_apk fail:' + b)
    else:
        raise Exception('zip_apk fail:apk not exist')

def parse_acti(activity):
    '''处理 从androidmanifest.xml中解析出activity标签 的函数'''
    hasMAIN = False;
    hasLAUNCHER = False;
    intent_filters = activity.getElementsByTagName("intent-filter");
    if not intent_filters or len(intent_filters)==0:
        return False;
    for intent_filter in intent_filters:
        hasMAIN = False;
        hasLAUNCHER = False;
        actions = intent_filter.getElementsByTagName("action");
        if actions and len(actions)>0:
            for action in actions:
                if action.hasAttribute("android:name"):
                    action_name = action.getAttribute("android:name");
                    if action_name == "android.intent.action.MAIN":
                        hasMAIN = True;
        categories = intent_filter.getElementsByTagName("category");
        if categories and len(categories)>0:
            for category in categories:
                if category.hasAttribute("android:name"):
                    category_name = category.getAttribute("android:name");
                    if category_name == "android.intent.category.LAUNCHER":
                        hasLAUNCHER = True;
        if hasMAIN and hasLAUNCHER:
            return hasMAIN and hasLAUNCHER;
    return hasMAIN and hasLAUNCHER;

def get_MainActivities(manifest_xml):
    print("manifest="+manifest_xml);
    DOMTree = minidom.parse(manifest_xml);
    manifest = DOMTree.documentElement
    pkgname = "";
    result = "";
    if manifest.hasAttribute("package"):
        pkgname = manifest.getAttribute("package");
    
    applications = manifest.getElementsByTagName("application");
    application = applications[0];#多个application只处理第一个
    
    activities = application.getElementsByTagName("activity");
    for activity in activities:
        if parse_acti(activity):
            acti_name = activity.getAttribute("android:name");
            if acti_name.startswith("."):
                acti_name = pkgname + acti_name;
            if result == "":
                result += acti_name;
            else:
                result += "||";
                result += acti_name;
    acti_alias = application.getElementsByTagName("activity-alias");
    for activity in acti_alias:
        if parse_acti(activity):
            acti_name = activity.getAttribute("android:targetActivity");
            if acti_name.startswith("."):
                acti_name = pkgname + acti_name;
            if result == "":
                result += acti_name;
            else:
                result += "||";
                result += acti_name;
    return result;

def getpackage_lanuch(inMF):
    """ 获取含有main和launcher的activity名字如果有多个用||分隔，如果没有返回""
    """
    if os.path.isfile(inMF):
        activities = get_MainActivities(inMF);
        if activities != "":
            return activities;
        else:
            raise Exception('getpackage_lanuch fail '+inMF)
    else:
        raise Exception('getpackage_lanuch fail:inMF not exist')

def get_code_snippet1(activityName):
    """ 生成代码片段一个方法会放到smali的最后
    """

    newActivity = activityName.replace(".","/")
    smali_snippet = '''
.method public com_sec_plugin_action_APP_STARTED()V
    .locals 2

    .prologue
    .line 68
    new-instance v0, Lcom/bangcle/everisk/shell/RiskStubShell;

    invoke-direct {v0}, Lcom/bangcle/everisk/shell/RiskStubShell;-><init>()V

    invoke-virtual {p0}, L''' + newActivity + ''';->getApplicationContext()Landroid/content/Context;

    move-result-object v1

    invoke-virtual {v0, v1}, Lcom/bangcle/everisk/shell/RiskStubShell;->load_in(Landroid/content/Context;)V

    .line 69
    return-void
.end method'''
    # return smali_snippet %(activityName.replace(".","/"))
    return smali_snippet

def get_code_snippet2(activityName):
    """ 生成代码片段的会被放到oncreate里的super.oncreate的后面
    """

    smali_snippet = '''
    invoke-virtual/range {p0 .. p0}, L%s;->com_sec_plugin_action_APP_STARTED()V'''

    return smali_snippet % (activityName.replace(".","/"))

def inject_smali(smali_dir, activity_name):
    """ 在一个smali的onCreate里的super.onCreate后面加上一些代码，
            如果这个smali文件里没有，就去找递归找它的父类
    """

    smali_file_path = os.path.join(smali_dir, activity_name.replace(".","/") + ".smali")
    print(smali_file_path)
    if not os.path.isfile(smali_file_path):
        raise Exception("inject_smali fail smali_file_path not exist")

    #存储新生成的smali文件
    newsmali_data = "";

    #它的父类
    father_class = None;

    #这个smali文件里是否有onCreate方法,如果没有就在其父类里找
    has_oncreate = False

    with open(smali_file_path) as smail_file:
        while 1:
            line = smail_file.readline()
            if not line:
                break
            #寻找其父类
            if line.startswith(".super"):
                father_class = line.lstrip(".super").strip()

            #在onCreate方法里的super.onCreate()的后面加上smali代码
            if line.find(" onCreate(Landroid/os/Bundle;)V") != -1:
                has_oncreate = True
                if not father_class:
                    raise Exception('the second line is not father_class')
                newsmali_data = newsmali_data + line;
                #找出含有super.oncreate的那一行
                while line.find(father_class + "->onCreate(Landroid/os/Bundle;)V") == -1 or line.find("invoke-super") == -1:
                    line = smail_file.readline()
                    if not line:
                        raise Exception('not find super.onCreate()')
                    newsmali_data = newsmali_data + line
                code = get_code_snippet2(activity_name)
                newsmali_data = newsmali_data + code
            else:
                newsmali_data = newsmali_data + line
                
    if has_oncreate:
        newsmali_data = newsmali_data + get_code_snippet1(activity_name)
        with open(smali_file_path,"wb") as smail_file:
            smail_file.write(newsmali_data)
    else:
        inject_smali(smali_dir, father_class[1:len(father_class)-1])

def copytree(src, dst):
    names = os.listdir(src)

    if not os.path.exists(dst):
        os.makedirs(dst)
    
    for name in names:
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        if os.path.isdir(srcname):
            copytree(srcname, dstname)
        else:
            if not os.path.exists(dstname):
                shutil.copy(srcname, dstname)
            else:
                raise Exception('有相同的类文件名')

def inject_12306_str_2_smali(file_name, agent):
    """ server_path     agent id 
    """
    if not os.path.isfile(file_name):
        raise Exception("文件不存在:"+file_name)

    newsmali_data = "";
    out2='%s'%(agent);

    agentid_key = "0x9527";

    flag = False
    f = open(file_name, 'r');
    for line in f.readlines():
        if line.find(agentid_key) != -1:
            i_agentid = string.atoi(out2);
            line = line.replace(agentid_key,hex(i_agentid)+"");
            flag = True;
        newsmali_data = newsmali_data + line;

    f.close();
    if flag:
        f = file(file_name,"w");
        f.write(newsmali_data);
        f.close();
    else:
        raise Exception("没有找到特征serveraddr或agentid");

def inject_ip_info_2_smali(file_name, ip1, ip2, standby):
    if not os.path.isfile(file_name):
        raise Exception("文件不存在:"+file_name)
    newsmali_data = "";
    rep_ip1     = "http://9.5.2.7";
    rep_ip2     = "http://9.5.2.8";
    rep_standby = "http://9.5.2.9";
    srv_defip	= "http://9.5.2.10def";
    dld_defip	= "http://9.5.2.11def";

    flag = False
    f = open(file_name, 'r');
    if standby == "":
        for line in f.readlines():
            if line.find(srv_defip) != -1:
                line = line.replace(srv_defip, ip1);
                flag = True;
            elif line.find(dld_defip) != -1:
                line = line.replace(dld_defip, ip2);
                flag = True;
            newsmali_data = newsmali_data + line;
    else:
        for line in f.readlines():
            if line.find(rep_ip1) != -1:
                line = line.replace(rep_ip1, ip1);
                flag = True;
            elif line.find(rep_ip2) != -1:
                line = line.replace(rep_ip2, ip2);
                flag = True;
            elif line.find(rep_standby) != -1:
                line = line.replace(rep_standby, standby);
                flag = True;
            elif line.find(srv_defip) != -1:
                line = line.replace(srv_defip, "");
                flag = True;
            elif line.find(dld_defip) != -1:
                line = line.replace(dld_defip, "");
                flag = True;
            newsmali_data = newsmali_data + line;
    f.close();
    if flag:
        f = file(file_name,'w');
        f.write(newsmali_data);
        f.close();
    else:
        raise Exception("没有找到特征ip或standby");

def inject_loader_delay_2_smali(filename, t1, t2):
    if not os.path.isfile(filename):
        raise Exception("文件不存在："+file_name);
    newsmali_data = "";
    t1_key = "0x9528";
    t2_key = "0x9529";
    t1_d = '%s'%(t1);
    t2_d = '%s'%(t2);
    flag = False;
    f = open(filename,'r');
    for line in f.readlines():
        if line.find(t1_key) != -1:
            line = line.replace(t1_key,hex(string.atoi(t1_d))+"");
            flag = True;
        elif line.find(t2_key) != -1:
            line = line.replace(t2_key,hex(string.atoi(t2_d))+"");
            flag = True;
        newsmali_data = newsmali_data + line;
    f.close();
    if flag:
        f = file(filename,"w");
        f.write(newsmali_data);
        f.close();
    else:
        raise Exception("没有找到特征t1或t2");




